package Logic;

//++++++++++++++++++++++++++++++++++++++++++++++++++++++//
//     Fuzzy Inference Engine SEDT2      //
//++++++++++++++++++++++++++++++++++++++++++++++++++++++//
public class SEDT2 implements FuzzyInferenceEngine {

    //+++++++++++++++++++++++++++++++++++++++++++++++++++++//
    //      Membership function of an input variable       //
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++//
    private abstract class InnerMembershipFunction {

        double min, max, step;

        abstract double param(int i);

        double center() {
            return 0;
        }

        double basis() {
            return 0;
        }

        abstract double isEqual(double x);

        double isSmallerOrEqual(double x) {
            double degree = 0, mu;
            for (double y = max; y >= x; y -= step) {
                if ((mu = isEqual(y)) > degree) {
                    degree = mu;
                }
            }
            return degree;
        }

        double isGreaterOrEqual(double x) {
            double degree = 0, mu;
            for (double y = min; y <= x; y += step) {
                if ((mu = isEqual(y)) > degree) {
                    degree = mu;
                }
            }
            return degree;
        }

        double isEqual(MembershipFunction mf) {
            if (mf instanceof FuzzySingleton) {
                return isEqual(((FuzzySingleton) mf).getValue());
            }
            if ((mf instanceof OutputMembershipFunction)
                    && ((OutputMembershipFunction) mf).isDiscrete()) {
                double[][] val = ((OutputMembershipFunction) mf).getDiscreteValues();
                double deg = 0;
                for (int i = 0; i < val.length; i++) {
                    double mu = isEqual(val[i][0]);
                    double minmu = (mu < val[i][1] ? mu : val[i][1]);
                    if (deg < minmu) {
                        deg = minmu;
                    }
                }
                return deg;
            }
            double mu1, mu2, minmu, degree = 0;
            for (double x = min; x <= max; x += step) {
                mu1 = mf.compute(x);
                mu2 = isEqual(x);
                minmu = (mu1 < mu2 ? mu1 : mu2);
                if (degree < minmu) {
                    degree = minmu;
                }
            }
            return degree;
        }

        double isGreaterOrEqual(MembershipFunction mf) {
            if (mf instanceof FuzzySingleton) {
                return isGreaterOrEqual(((FuzzySingleton) mf).getValue());
            }
            if ((mf instanceof OutputMembershipFunction)
                    && ((OutputMembershipFunction) mf).isDiscrete()) {
                double[][] val = ((OutputMembershipFunction) mf).getDiscreteValues();
                double deg = 0;
                for (int i = 0; i < val.length; i++) {
                    double mu = isGreaterOrEqual(val[i][0]);
                    double minmu = (mu < val[i][1] ? mu : val[i][1]);
                    if (deg < minmu) {
                        deg = minmu;
                    }
                }
                return deg;
            }
            double mu1, mu2, minmu, degree = 0, greq = 0;
            for (double x = min; x <= max; x += step) {
                mu1 = mf.compute(x);
                mu2 = isEqual(x);
                if (mu2 > greq) {
                    greq = mu2;
                }
                if (mu1 < greq) {
                    minmu = mu1;
                } else {
                    minmu = greq;
                }
                if (degree < minmu) {
                    degree = minmu;
                }
            }
            return degree;
        }

        double isSmallerOrEqual(MembershipFunction mf) {
            if (mf instanceof FuzzySingleton) {
                return isSmallerOrEqual(((FuzzySingleton) mf).getValue());
            }
            if ((mf instanceof OutputMembershipFunction)
                    && ((OutputMembershipFunction) mf).isDiscrete()) {
                double[][] val = ((OutputMembershipFunction) mf).getDiscreteValues();
                double deg = 0;
                for (int i = 0; i < val.length; i++) {
                    double mu = isSmallerOrEqual(val[i][0]);
                    double minmu = (mu < val[i][1] ? mu : val[i][1]);
                    if (deg < minmu) {
                        deg = minmu;
                    }
                }
                return deg;
            }
            double mu1, mu2, minmu, degree = 0, smeq = 0;
            for (double x = max; x >= min; x -= step) {
                mu1 = mf.compute(x);
                mu2 = isEqual(x);
                if (mu2 > smeq) {
                    smeq = mu2;
                }
                if (mu1 < smeq) {
                    minmu = mu1;
                } else {
                    minmu = smeq;
                }
                if (degree < minmu) {
                    degree = minmu;
                }
            }
            return degree;
        }

        double isGreater(MembershipFunction mf, InnerOperatorset op) {
            if (mf instanceof FuzzySingleton) {
                return op.not(isSmallerOrEqual(((FuzzySingleton) mf).getValue()));
            }
            if ((mf instanceof OutputMembershipFunction)
                    && ((OutputMembershipFunction) mf).isDiscrete()) {
                double[][] val = ((OutputMembershipFunction) mf).getDiscreteValues();
                double deg = 0;
                for (int i = 0; i < val.length; i++) {
                    double mu = op.not(isSmallerOrEqual(val[i][0]));
                    double minmu = (mu < val[i][1] ? mu : val[i][1]);
                    if (deg < minmu) {
                        deg = minmu;
                    }
                }
                return deg;
            }
            double mu1, mu2, minmu, gr, degree = 0, smeq = 0;
            for (double x = max; x >= min; x -= step) {
                mu1 = mf.compute(x);
                mu2 = isEqual(x);
                if (mu2 > smeq) {
                    smeq = mu2;
                }
                gr = op.not(smeq);
                minmu = (mu1 < gr ? mu1 : gr);
                if (degree < minmu) {
                    degree = minmu;
                }
            }
            return degree;
        }

        double isSmaller(MembershipFunction mf, InnerOperatorset op) {
            if (mf instanceof FuzzySingleton) {
                return op.not(isGreaterOrEqual(((FuzzySingleton) mf).getValue()));
            }
            if ((mf instanceof OutputMembershipFunction)
                    && ((OutputMembershipFunction) mf).isDiscrete()) {
                double[][] val = ((OutputMembershipFunction) mf).getDiscreteValues();
                double deg = 0;
                for (int i = 0; i < val.length; i++) {
                    double mu = op.not(isGreaterOrEqual(val[i][0]));
                    double minmu = (mu < val[i][1] ? mu : val[i][1]);
                    if (deg < minmu) {
                        deg = minmu;
                    }
                }
                return deg;
            }
            double mu1, mu2, minmu, sm, degree = 0, greq = 0;
            for (double x = min; x <= max; x += step) {
                mu1 = mf.compute(x);
                mu2 = isEqual(x);
                if (mu2 > greq) {
                    greq = mu2;
                }
                sm = op.not(greq);
                minmu = (mu1 < sm ? mu1 : sm);
                if (degree < minmu) {
                    degree = minmu;
                }
            }
            return degree;
        }

        double isNotEqual(MembershipFunction mf, InnerOperatorset op) {
            if (mf instanceof FuzzySingleton) {
                return op.not(isEqual(((FuzzySingleton) mf).getValue()));
            }
            if ((mf instanceof OutputMembershipFunction)
                    && ((OutputMembershipFunction) mf).isDiscrete()) {
                double[][] val = ((OutputMembershipFunction) mf).getDiscreteValues();
                double deg = 0;
                for (int i = 0; i < val.length; i++) {
                    double mu = op.not(isEqual(val[i][0]));
                    double minmu = (mu < val[i][1] ? mu : val[i][1]);
                    if (deg < minmu) {
                        deg = minmu;
                    }
                }
                return deg;
            }
            double mu1, mu2, minmu, degree = 0;
            for (double x = min; x <= max; x += step) {
                mu1 = mf.compute(x);
                mu2 = op.not(isEqual(x));
                minmu = (mu1 < mu2 ? mu1 : mu2);
                if (degree < minmu) {
                    degree = minmu;
                }
            }
            return degree;
        }

        double isApproxEqual(MembershipFunction mf, InnerOperatorset op) {
            if (mf instanceof FuzzySingleton) {
                return op.moreorless(isEqual(((FuzzySingleton) mf).getValue()));
            }
            if ((mf instanceof OutputMembershipFunction)
                    && ((OutputMembershipFunction) mf).isDiscrete()) {
                double[][] val = ((OutputMembershipFunction) mf).getDiscreteValues();
                double deg = 0;
                for (int i = 0; i < val.length; i++) {
                    double mu = op.moreorless(isEqual(val[i][0]));
                    double minmu = (mu < val[i][1] ? mu : val[i][1]);
                    if (deg < minmu) {
                        deg = minmu;
                    }
                }
                return deg;
            }
            double mu1, mu2, minmu, degree = 0;
            for (double x = min; x <= max; x += step) {
                mu1 = mf.compute(x);
                mu2 = op.moreorless(isEqual(x));
                minmu = (mu1 < mu2 ? mu1 : mu2);
                if (degree < minmu) {
                    degree = minmu;
                }
            }
            return degree;
        }

        double isVeryEqual(MembershipFunction mf, InnerOperatorset op) {
            if (mf instanceof FuzzySingleton) {
                return op.very(isEqual(((FuzzySingleton) mf).getValue()));
            }
            if ((mf instanceof OutputMembershipFunction)
                    && ((OutputMembershipFunction) mf).isDiscrete()) {
                double[][] val = ((OutputMembershipFunction) mf).getDiscreteValues();
                double deg = 0;
                for (int i = 0; i < val.length; i++) {
                    double mu = op.very(isEqual(val[i][0]));
                    double minmu = (mu < val[i][1] ? mu : val[i][1]);
                    if (deg < minmu) {
                        deg = minmu;
                    }
                }
                return deg;
            }
            double mu1, mu2, minmu, degree = 0;
            for (double x = min; x <= max; x += step) {
                mu1 = mf.compute(x);
                mu2 = op.very(isEqual(x));
                minmu = (mu1 < mu2 ? mu1 : mu2);
                if (degree < minmu) {
                    degree = minmu;
                }
            }
            return degree;
        }

        double isSlightlyEqual(MembershipFunction mf, InnerOperatorset op) {
            if (mf instanceof FuzzySingleton) {
                return op.slightly(isEqual(((FuzzySingleton) mf).getValue()));
            }
            if ((mf instanceof OutputMembershipFunction)
                    && ((OutputMembershipFunction) mf).isDiscrete()) {
                double[][] val = ((OutputMembershipFunction) mf).getDiscreteValues();
                double deg = 0;
                for (int i = 0; i < val.length; i++) {
                    double mu = op.slightly(isEqual(val[i][0]));
                    double minmu = (mu < val[i][1] ? mu : val[i][1]);
                    if (deg < minmu) {
                        deg = minmu;
                    }
                }
                return deg;
            }
            double mu1, mu2, minmu, degree = 0;
            for (double x = min; x <= max; x += step) {
                mu1 = mf.compute(x);
                mu2 = op.slightly(isEqual(x));
                minmu = (mu1 < mu2 ? mu1 : mu2);
                if (degree < minmu) {
                    degree = minmu;
                }
            }
            return degree;
        }
    }

    //+++++++++++++++++++++++++++++++++++++++++++++++++++++//
    //          Abstract class of an operator set          //
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++//
    private abstract class InnerOperatorset {

        abstract double and(double a, double b);

        abstract double or(double a, double b);

        abstract double also(double a, double b);

        abstract double imp(double a, double b);

        abstract double not(double a);

        abstract double very(double a);

        abstract double moreorless(double a);

        abstract double slightly(double a);

        abstract double defuz(OutputMembershipFunction mf);
    }

    //+++++++++++++++++++++++++++++++++++++++++++++++++++++//
    //      Class for the conclusion of a fuzzy rule       //
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++//
    private class InnerConclusion {

        private double degree;
        private InnerMembershipFunction mf;
        private InnerOperatorset op;

        InnerConclusion(double degree, InnerMembershipFunction mf, InnerOperatorset op) {
            this.op = op;
            this.degree = degree;
            this.mf = mf;
        }

        public double degree() {
            return degree;
        }

        public double compute(double x) {
            return op.imp(degree, mf.isEqual(x));
        }

        public double center() {
            return mf.center();
        }

        public double basis() {
            return mf.basis();
        }

        public double param(int i) {
            return mf.param(i);
        }

        public double min() {
            return mf.min;
        }

        public double max() {
            return mf.max;
        }

        public double step() {
            return mf.step;
        }

        public boolean isSingleton() {
            return mf.getClass().getName().endsWith("MF_xfl_singleton");
        }
    }

    //+++++++++++++++++++++++++++++++++++++++++++++++++++++//
    //      Membership function of an output variable      //
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++//
    private class OutputMembershipFunction implements MembershipFunction {

        public InnerConclusion[] conc;
        public double[] input;
        private InnerOperatorset op;

        OutputMembershipFunction() {
            this.conc = new InnerConclusion[0];
        }

        public void set(int size, InnerOperatorset op, double[] input) {
            this.input = input;
            this.op = op;
            this.conc = new InnerConclusion[size];
        }

        public void set(int pos, double dg, InnerMembershipFunction imf) {
            this.conc[pos] = new InnerConclusion(dg, imf, op);
        }

        public double compute(double x) {
            double dom = conc[0].compute(x);
            for (int i = 1; i < conc.length; i++) {
                dom = op.also(dom, conc[i].compute(x));
            }
            return dom;
        }

        public double defuzzify() {
            return op.defuz(this);
        }

        public double min() {
            return conc[0].min();
        }

        public double max() {
            return conc[0].max();
        }

        public double step() {
            return conc[0].step();
        }

        public boolean isDiscrete() {
            for (int i = 0; i < conc.length; i++) {
                if (!conc[i].isSingleton()) {
                    return false;
                }
            }
            return true;
        }

        public double[][] getDiscreteValues() {
            double[][] value = new double[conc.length][2];
            for (int i = 0; i < conc.length; i++) {
                value[i][0] = conc[i].param(0);
                value[i][1] = conc[i].degree();
            }
            return value;
        }
    }

    //+++++++++++++++++++++++++++++++++++++++++++++++++++++//
    //      Membership function MF_xfl_trapezoid      //
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++//
    private class MF_xfl_trapezoid extends InnerMembershipFunction {

        double a;
        double b;
        double c;
        double d;

        MF_xfl_trapezoid(double min, double max, double step, double param[]) {
            super.min = min;
            super.max = max;
            super.step = step;
            this.a = param[0];
            this.b = param[1];
            this.c = param[2];
            this.d = param[3];
        }

        double param(int _i) {
            switch (_i) {
                case 0:
                    return a;
                case 1:
                    return b;
                case 2:
                    return c;
                case 3:
                    return d;
                default:
                    return 0;
            }
        }

        double isEqual(double x) {
            return (x < a || x > d ? 0 : (x < b ? (x - a) / (b - a) : (x < c ? 1 : (d - x) / (d - c))));
        }

        double isGreaterOrEqual(double x) {
            return (x < a ? 0 : (x > b ? 1 : (x - a) / (b - a)));
        }

        double isSmallerOrEqual(double x) {
            return (x < c ? 1 : (x > d ? 0 : (d - x) / (d - c)));
        }

        double center() {
            return (b + c) / 2;
        }

        double basis() {
            return (d - a);
        }
    }

    //+++++++++++++++++++++++++++++++++++++++++++++++++++++//
    //     Operator set OP_operadoRes      //
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++//
    private class OP_operadoRes extends InnerOperatorset {

        double and(double a, double b) {
            return (a < b ? a : b);
        }

        double or(double a, double b) {
            return (a > b ? a : b);
        }

        double also(double a, double b) {
            return (a > b ? a : b);
        }

        double imp(double a, double b) {
            return (a < b ? a : b);
        }

        double not(double a) {
            return 1 - a;
        }

        double very(double a) {
            double w = 2.0;
            return Math.pow(a, w);
        }

        double moreorless(double a) {
            double w = 0.5;
            return Math.pow(a, w);
        }

        double slightly(double a) {
            return 4 * a * (1 - a);
        }

        double defuz(OutputMembershipFunction mf) {
            double min = mf.min();
            double max = mf.max();
            double step = mf.step();
            double num = 0, denom = 0;
            for (double x = min; x <= max; x += step) {
                double m = mf.compute(x);
                num += x * m;
                denom += m;
            }
            if (denom == 0) {
                return (min + max) / 2;
            }
            return num / denom;
        }
    }

    //+++++++++++++++++++++++++++++++++++++++++++++++++++++//
    //     Operator set OP__default_      //
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++//
    private class OP__default_ extends InnerOperatorset {

        double and(double a, double b) {
            return (a < b ? a : b);
        }

        double or(double a, double b) {
            return (a > b ? a : b);
        }

        double also(double a, double b) {
            return (a > b ? a : b);
        }

        double imp(double a, double b) {
            return (a < b ? a : b);
        }

        double not(double a) {
            return 1 - a;
        }

        double very(double a) {
            double w = 2.0;
            return Math.pow(a, w);
        }

        double moreorless(double a) {
            double w = 0.5;
            return Math.pow(a, w);
        }

        double slightly(double a) {
            return 4 * a * (1 - a);
        }

        double defuz(OutputMembershipFunction mf) {
            double min = mf.min();
            double max = mf.max();
            double step = mf.step();
            double num = 0, denom = 0;
            for (double x = min; x <= max; x += step) {
                double m = mf.compute(x);
                num += x * m;
                denom += m;
            }
            if (denom == 0) {
                return (min + max) / 2;
            }
            return num / denom;
        }
    }

    //+++++++++++++++++++++++++++++++++++++++++++++++++++++//
    //  Type TP_unidadesCalor  //
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++//
    private class TP_unidadesCalor {

        private double min = 0.0;
        private double max = 350.0;
        private double step = 0.10002857959416976;
        double _p_pocas[] = {0.0, 49.0, 59.0, 86.0};
        double _p_moderadas[] = {60.0, 87.0, 143.0, 193.0};
        double _p_optimas[] = {144.0, 194.0, 268.0, 303.0};
        double _p_elevadas[] = {270.0, 304.0, 349.99, 350.0};
        MF_xfl_trapezoid pocas = new MF_xfl_trapezoid(min, max, step, _p_pocas);
        MF_xfl_trapezoid moderadas = new MF_xfl_trapezoid(min, max, step, _p_moderadas);
        MF_xfl_trapezoid optimas = new MF_xfl_trapezoid(min, max, step, _p_optimas);
        MF_xfl_trapezoid elevadas = new MF_xfl_trapezoid(min, max, step, _p_elevadas);
    }

    //+++++++++++++++++++++++++++++++++++++++++++++++++++++//
    //  Type TP_procentaje  //
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++//
    private class TP_procentaje {

        private double min = 0.0;
        private double max = 100.0;
        private double step = 0.1001001001001001;
        double _p_pocoProbable[] = {0.0, 14.0, 30.0, 49.0};
        double _p_probable[] = {21.0, 50.0, 66.0, 77.0};
        double _p_muyProbable[] = {67.0, 78.0, 99.99, 100.0};
        MF_xfl_trapezoid pocoProbable = new MF_xfl_trapezoid(min, max, step, _p_pocoProbable);
        MF_xfl_trapezoid probable = new MF_xfl_trapezoid(min, max, step, _p_probable);
        MF_xfl_trapezoid muyProbable = new MF_xfl_trapezoid(min, max, step, _p_muyProbable);
    }

    //+++++++++++++++++++++++++++++++++++++++++++++++++++++//
    //  Rulebase RL_RuleBTrips  //
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++//
    private MembershipFunction[] RL_RuleBTrips(MembershipFunction unidadesCalor) {
        double _rl;
        double _input[] = new double[1];
        if (unidadesCalor instanceof FuzzySingleton) {
            _input[0] = ((FuzzySingleton) unidadesCalor).getValue();
        }
        OP__default_ _op = new OP__default_();
        OutputMembershipFunction porcentaje = new OutputMembershipFunction();
        porcentaje.set(4, _op, _input);
        TP_unidadesCalor _t_unidadesCalor = new TP_unidadesCalor();
        TP_procentaje _t_porcentaje = new TP_procentaje();
        int _i_porcentaje = 0;
        _rl = _t_unidadesCalor.pocas.isEqual(unidadesCalor);
        porcentaje.set(_i_porcentaje, _rl, _t_porcentaje.pocoProbable);
        _i_porcentaje++;
        _rl = _t_unidadesCalor.moderadas.isEqual(unidadesCalor);
        porcentaje.set(_i_porcentaje, _rl, _t_porcentaje.probable);
        _i_porcentaje++;
        _rl = _t_unidadesCalor.optimas.isEqual(unidadesCalor);
        porcentaje.set(_i_porcentaje, _rl, _t_porcentaje.muyProbable);
        _i_porcentaje++;
        _rl = _t_unidadesCalor.elevadas.isEqual(unidadesCalor);
        porcentaje.set(_i_porcentaje, _rl, _t_porcentaje.pocoProbable);
        _i_porcentaje++;
        MembershipFunction[] _output = new MembershipFunction[1];
        _output[0] = porcentaje;
        return _output;
    }

    //+++++++++++++++++++++++++++++++++++++++++++++++++++++//
    //               Fuzzy Inference Engine                //
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++//
    public double[] crispInference(double[] _input) {
   MembershipFunction uniCalor = new FuzzySingleton(_input[0]);
  MembershipFunction porcenTaje;
  MembershipFunction[] _call;
  _call = RL_RuleBTrips(uniCalor); porcenTaje=_call[0];
  double _output[] = new double[1];
  if(porcenTaje instanceof FuzzySingleton)
   _output[0] = ((FuzzySingleton) porcenTaje).getValue();
  else _output[0] = ((OutputMembershipFunction) porcenTaje).defuzzify();
  return _output;
    }

    public double[] crispInference(MembershipFunction[] _input) {
        MembershipFunction uniCalor = _input[0];
        MembershipFunction porcenTaje;
        MembershipFunction[] _call;
        _call = RL_RuleBTrips(uniCalor);
        porcenTaje = _call[0];
        double _output[] = new double[1];
        if (porcenTaje instanceof FuzzySingleton) {
            _output[0] = ((FuzzySingleton) porcenTaje).getValue();
        } else {
            _output[0] = ((OutputMembershipFunction) porcenTaje).defuzzify();
        }
        return _output;
    }

    public MembershipFunction[] fuzzyInference(double[] _input) {
        MembershipFunction uniCalor = new FuzzySingleton(_input[0]);
        MembershipFunction porcenTaje;
        MembershipFunction[] _call;
        _call = RL_RuleBTrips(uniCalor);
        porcenTaje = _call[0];
        MembershipFunction _output[] = new MembershipFunction[1];
        _output[0] = porcenTaje;
        return _output;
    }

    public MembershipFunction[] fuzzyInference(MembershipFunction[] _input) {
        MembershipFunction uniCalor = _input[0];
        MembershipFunction porcenTaje;
        MembershipFunction[] _call;
        _call = RL_RuleBTrips(uniCalor);
        porcenTaje = _call[0];
        MembershipFunction _output[] = new MembershipFunction[1];
        _output[0] = porcenTaje;
        return _output;
    }
}
